package com.turing.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class CoordinateUtil {

    private static final Log log = LogFactory.getLog(CoordinateUtil.class);

    private static final int MISTAKE_FACTOR = 3;

    /**
     * @param elementObj：标注信息JSON
     * @param pdfJson：orc完成之后的JSONArray
     */
    public List<PageVo> getCoordinates(JSONObject elementObj, JSONArray pdfJson) {

        List<PageVo> pageVos = new ArrayList<>();
        try {
            if (null != elementObj.get("result") && !StringUtils.isEmpty(elementObj.get("result"))) {
                //不用JSONObject.toJSONString处理会报错
                JSONArray resultList = JSON.parseArray(JSONObject.toJSONString(elementObj.get("result")));
                if (!StringUtils.isEmpty(resultList)) {
                    for (Object o : resultList) {
                        if (null != o) {
                            JSONObject resultObj = JSONObject.parseObject(o.toString());
                            JSONArray posInfoArray = resultObj.getJSONArray("pos_info");
                            Object para_info_obj = resultObj.get("para_info");
                            if (null != para_info_obj) {
                                JSONArray para_info_list = JSON.parseArray(para_info_obj.toString());
                                if (!StringUtils.isEmpty(para_info_list)) {
                                    for (int j = 0; j < para_info_list.size(); j++) {
                                        int start = 0;
                                        int end = -1;
                                        if (j == 0) {
                                            start = Integer.parseInt(posInfoArray.get(0).toString());
                                        }
                                        if (j == para_info_list.size() - 1) {
                                            end = Integer.parseInt(posInfoArray.get(1).toString());
                                        }
                                        PageVo pageVo = getCoordinate(start, end, pdfJson, para_info_list.get(j).toString());
                                        if (pageVo != null) {
                                            pageVos.add(pageVo);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } else {
                log.info("抽取结果为空");
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!CollectionUtils.isEmpty(pageVos)) {
            log.info("分页坐标信息为: " + pageVos);
            return pageVos;
        }

        log.info("抽取结果为空: " + pageVos);
        return null;
    }

    public void mergePageCoordinates(List<PageVo> pageVos, List<PageVo> result) {

        if (!CollectionUtils.isEmpty(pageVos)) {

            Map<Integer, List<PageVo>> pageMap = pageVos.stream().collect(Collectors.groupingBy(PageVo::getPageNo));
            for (Map.Entry<Integer, List<PageVo>> ListEntry : pageMap.entrySet()) {
                Integer key = ListEntry.getKey();
                List<PageVo> value = ListEntry.getValue();
                PageVo pageVo = new PageVo();
                pageVo.setPageNo(key);
                List<Coordinate> coordinatesNew = new ArrayList<>();
                for (PageVo vo : value) {
                    List<Coordinate> coordinates = vo.getCoordinates();
                    coordinatesNew.addAll(coordinates);
                }
                pageVo.setCoordinates(coordinatesNew);
                result.add(pageVo);
            }
        }

    }

    //从ocr结果中获取单行文本
    private PageVo getCoordinate(int start, int end, JSONArray pdfJson, String para_info) {

        if (!StringUtils.isEmpty(para_info)) {
            int pageNo = 0;
            String[] paraInfos = para_info.split("_");
            if (isNumeric(paraInfos[1])) {
                pageNo = Integer.parseInt(paraInfos[1]);
            }
            Object pageJson = pdfJson.get(pageNo);
            if (null != pageJson) {
                JSONObject pageObj = JSONObject.parseObject(pageJson.toString());
                if (null != pageObj) {
                    Object paragraphsObj = pageObj.get("paragraphs");
                    if (null != paragraphsObj) {
                        JSONArray paragraphsArray = JSON.parseArray(paragraphsObj.toString());
                        if (!StringUtils.isEmpty(paragraphsArray)) {
                            if (null != paragraphsArray.get(0)) {
                                JSONObject paragraphObj = JSONObject.parseObject(paragraphsArray.get(0).toString());
                                JSONArray lineArray = JSON.parseArray(paragraphObj.get("lines").toString());
                                if (!StringUtils.isEmpty(lineArray)) {
                                    for (Object lineObj : lineArray) {
                                        if (null != lineObj) {
                                            JSONObject lineJson = JSONObject.parseObject(lineObj.toString());
                                            if (null != lineJson.get("objContent")) {
                                                if (lineJson.get("objContent") instanceof JSONArray) {
                                                    JSONArray objContentArray = JSON.parseArray(lineJson.get("objContent").toString());
                                                    for (Object objContent : objContentArray) {
                                                        if (null != objContent) {
                                                            JSONObject objContentJson = JSONObject.parseObject(objContent.toString());
                                                            if (null != objContentJson.get("lineId") && objContentJson.get("lineId").toString().equals(para_info)
                                                                    && null != objContentJson.get("objContent") && null != objContentJson.get("objPos")) {
                                                                Coordinate Coordinate = parseCoordinate(objContentJson.get("objPos"));
                                                                String lineText = objContentJson.get("objContent").toString();
                                                                return calculateCoordinate(start, end, pageNo, lineText, Coordinate);
                                                            }
                                                        }
                                                    }
                                                } else if (null != lineJson.get("lineId") && lineJson.get("lineId").toString().equals(para_info) && null != lineJson.get("objPos")) {
                                                    Coordinate Coordinate = parseCoordinate(lineJson.get("objPos"));
                                                    String lineText = lineJson.get("objContent").toString();
                                                    return calculateCoordinate(start, end, pageNo, lineText, Coordinate);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return null;

    }

    //将字符串解析为坐标对象
    private Coordinate parseCoordinate(Object objPos) {
        Coordinate coordinate = new Coordinate();
        JSONArray posArray = JSON.parseArray(objPos.toString());
        if (!CollectionUtils.isEmpty(posArray)) {

//            coordinate.setHorizontal(Integer.parseInt(posArray.get(0).toString()));
//            coordinate.setOrdinate(Integer.parseInt(posArray.get(1).toString()));
//            coordinate.setHeight(Integer.parseInt(posArray.get(2).toString()));
//            coordinate.setWidth(Integer.parseInt(posArray.get(3).toString()));
            coordinate.setHorizontal(posArray.getInteger(0));
            coordinate.setOrdinate(posArray.getInteger(1));
            coordinate.setHeight(posArray.getInteger(2));
            coordinate.setWidth(posArray.getInteger(3));
        }
        return coordinate;
    }

    /*
     * 把完整的pdf行分成三段，分别是标注文字前段，标注文字段和标注文字后段，
     * 每段统计中文汉字字符数量和非中文汉字和数字和汉字间的空格数量
     * 其中中文字符算2个占位,非中文字符算1个占位,数字和汉字间的空格占0.5个占位
     * 计算出标注文字起始水平坐标和标注文字宽度
     * 最后把原始整行的纵向长度和纵坐标赋值给标注文本的纵向长度和纵坐标,封装上标注文本所属的页码返回
     * */
    private PageVo calculateCoordinate(int start, int end, int pageNo, String ocrText, Coordinate sourceCoordinate) {

        PageVo pageVo = new PageVo();
        List<Coordinate> coordinates = new ArrayList<>();
        Coordinate coordinate = new Coordinate();
        coordinate.setHeight(sourceCoordinate.getHeight());
        coordinate.setHorizontal(sourceCoordinate.getHorizontal());

        //标注文本之前、标注文本中和标注文本之后中文和非中文字符数量以及汉字与数字之间的空格数量计数器（3*3）
        int preBlanks = 0;
        int markBlanks = 0;
        int postBlanks = 0;
        int preNumChinese = 0;
        int markNumChinese = 0;
        int postNumChinese = 0;
        int preNumNonChinese = 0;
        int markNumNonChinese = 0;
        int postNumNonChinese = 0;
        if (end == -1) {
            end = ocrText.length();
        }

        //进行统计
        String regChin = "^[\u4e00-\u9fa5]{1}$";//汉字
        String reg = "[^\\x00-\\xff]";//双字节字符
        String regNum = "[0-9]";//数字
        if (start != 0) {
            for (int i = 0; i < start; i++) {
                String str = Character.toString(ocrText.charAt(i));
                String nextStr = "";
                if (i < ocrText.length() - 1) {
                    nextStr = Character.toString(ocrText.charAt(i + 1));
                }
                if (str.matches(reg) || isDoubleOccupy(str)) {
                    preNumChinese++;
                    if (nextStr.matches(regNum) && str.matches(regChin)) {
                        preBlanks++;
                    }
                } else {
                    preNumNonChinese++;
                    if (nextStr.matches(regChin) && str.matches(regNum)) {
                        preBlanks++;
                    }
                }
            }
        }

        for (int i = start; i < end; i++) {
            String str = Character.toString(ocrText.charAt(i));
            String nextStr = "";
            if (i < ocrText.length() - 1) {
                nextStr = Character.toString(ocrText.charAt(i + 1));
            }
            if (str.matches(reg) || isDoubleOccupy(str)) {
                markNumChinese++;
                if (nextStr.matches(regNum) && str.matches(regChin)) {
                    markBlanks++;
                }
            } else {
                markNumNonChinese++;
                if (nextStr.matches(regChin) && str.matches(regNum)) {
                    markBlanks++;
                }
            }
        }

        if (end != ocrText.length()) {
            for (int i = end; i < ocrText.length(); i++) {
                String str = Character.toString(ocrText.charAt(i));
                String nextStr = "";
                if (i < ocrText.length() - 1) {
                    nextStr = Character.toString(ocrText.charAt(i + 1));
                }
                if (str.matches(reg) || isDoubleOccupy(str)) {
                    postNumChinese++;
                    if (nextStr.matches(regNum) && str.matches(regChin)) {
                        postBlanks++;
                    }
                } else {
                    postNumNonChinese++;
                    if (nextStr.matches(regChin) && str.matches(regNum)) {
                        postBlanks++;
                    }
                }
            }
        }

        //总像素长度
        double sum = (preBlanks + markBlanks + postBlanks) * 0.5 +
                (preNumChinese + markNumChinese + postNumChinese) * 2 +
                (preNumNonChinese + markNumNonChinese + postNumNonChinese);

        //计算误差，让标注结果居中
        double mistake = sourceCoordinate.getWidth() / sum / MISTAKE_FACTOR;

        //计算标注文本长度
        //长度占比
        double markProportion = (markBlanks * 0.5 + markNumChinese * 2 + markNumNonChinese) / sum;
        int length = (int) (sourceCoordinate.getWidth() * markProportion);
        coordinate.setWidth(length);

        //如果有，计算起始长度
        if (start != 0) {
            double preProportion = (preBlanks * 0.5 + preNumChinese * 2 + preNumNonChinese) / sum;
            int preLength = (int) (sourceCoordinate.getWidth() * preProportion);
            coordinate.setOrdinate((int) (sourceCoordinate.getOrdinate() + preLength + mistake));
        } else {
            coordinate.setOrdinate((int) (sourceCoordinate.getOrdinate() + mistake));
        }

        coordinates.add(coordinate);
        log.info("生成坐标成功，坐标为： 起始点X轴：" + coordinate.getOrdinate() + " 起始点y轴：" + coordinate.getHorizontal()
                + " 标注块长度：" + coordinate.getWidth() + " 标注块高度：" + coordinate.getHeight());
        pageVo.setPageNo(pageNo);
        pageVo.setCoordinates(coordinates);
        return pageVo;

    }

    //判断是否为数字
    private boolean isNumeric(String str) {

        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        return isNum.matches();

    }

    //判断是否是二占位字符 （ ） @ % ,
    private boolean isDoubleOccupy(String str) {

        List doubleOccupyChars = Arrays.asList(215, 247, 64, 37, 44);
        if (!StringUtils.isEmpty(str)) {
            int ascii = str.charAt(0);
            return doubleOccupyChars.contains(ascii);
        }
        return false;
    }

    //判断是否是括号
    /*private boolean isParenthesis(String str){

        List parentheses = Arrays.asList(65288,65289);
        if(!StringUtils.isEmpty(str)){
            int ascii = str.charAt(0);
            return parentheses.contains(ascii);
        }
        return false;
    }*/

    public List<PageVo> getDefaultResultPage(JSONObject elementObj) {

        List<PageVo> resultPage = new ArrayList<>();
        PageVo pageVo = new PageVo();
        int defaultPageNo = -1;

        if (null != elementObj.get("result") && !StringUtils.isEmpty(elementObj.get("result"))) {
            //不用JSONObject.toJSONString处理会报错
            JSONArray resultList = JSON.parseArray(JSONObject.toJSONString(elementObj.get("result")));
            if (!StringUtils.isEmpty(resultList)) {
                for (Object o : resultList) {
                    if (null != o) {
                        JSONObject resultObj = JSONObject.parseObject(o.toString());
                        defaultPageNo = resultObj.getInteger("page");
                        break;
                    }
                }
            }
        }

        if (defaultPageNo != -1) {

            Coordinate defaultCoordinate = new Coordinate();
            defaultCoordinate.setOrdinate(0);
            defaultCoordinate.setHorizontal(0);
            defaultCoordinate.setHeight(0);
            defaultCoordinate.setWidth(0);

            List<Coordinate> defaultCoordinates = new ArrayList<>();
            defaultCoordinates.add(defaultCoordinate);
            pageVo.setPageNo(defaultPageNo);
            pageVo.setCoordinates(defaultCoordinates);
            resultPage.add(pageVo);
        }


        return resultPage;

    }

    @Data
    public class Coordinate {

        private int horizontal;
        private int ordinate;
        private int height;
        private int width;

    }

    @Data
    public class PageVo {

        private Integer pageNo;
        private List<Coordinate> coordinates;

    }

}
